﻿
Imports System.Threading
Imports std

Partial Public Class callbackAction
    Implements ICloneable
    Private Shared ReadOnly CALLBACKACTION_CHECK_QUEUE_LENGTH As Int64
    Private Shared ReadOnly CALLBACKACTION_CHECK_QUEUE_TIMEMS As Int64
    Private Shared ReadOnly CALLBACKACTION_CHECK_QUEUE_INTERVAL_TIMEMS As Int64
    Private begin As _do(Of Boolean) = Nothing
    Private check As _do(Of Boolean) = Nothing
    Private [end] As _do(Of Boolean) = Nothing
    Private before_step As before_step_t = Nothing
    Private timeoutticks As Int64 = npos
    Private _callstack As String = Nothing

    Private Enum before_step_t As Byte
        begin
        check
        [end]
        finished
    End Enum

    Protected Sub trigger_check()
        manager().trigger_check()
    End Sub

    Public Function finished() As Boolean
        Return before_step = before_step_t.finished
    End Function

    Public Function pending() As Boolean
        Return before_step = before_step_t.check
    End Function

    Public Function begining() As Boolean
        Return before_step = before_step_t.begin
    End Function

    Public Function ending() As Boolean
        Return before_step = before_step_t.end
    End Function

    Private Sub to_begining()
        before_step = before_step_t.begin
    End Sub

    Private Sub to_pending()
        before_step = before_step_t.check
    End Sub

    Private Sub to_ending()
        before_step = before_step_t.end
    End Sub

    Private Sub to_finished()
        before_step = before_step_t.finished
    End Sub

    Private _manager As icallbackManager = Nothing
    Friend Property manager As icallbackManager
        Private Get
            assert(Not _manager Is Nothing)
            Return _manager
        End Get
        Set(ByVal value As icallbackManager)
            assert(Not value Is Nothing)
            assert(_manager Is Nothing)
            _manager = value
        End Set
    End Property

    Private _begin_result As ternary = ternary.unknown
    Public Property begin_result As ternary
        Get
            Return _begin_result
        End Get
        Private Set(ByVal value As ternary)
            assert(_end_result.unknown_() OrElse value.unknown_())
            _begin_result = value
        End Set
    End Property

    Private _check_result As ternary = ternary.unknown
    Public Property check_result As ternary
        Get
            Return _check_result
        End Get
        Private Set(ByVal value As ternary)
            assert(_check_result.nottrue() OrElse value.unknown_())
            _check_result = value
        End Set
    End Property

    Private _end_result As ternary = ternary.unknown
    Public Property end_result As ternary
        Get
            Return _end_result
        End Get
        Private Set(ByVal value As ternary)
            assert(_end_result.unknown_() OrElse value.unknown_())
            _end_result = value
        End Set
    End Property

    Private _ctor_ticks As Int64 = npos
    Public Property ctor_ticks As Int64
        Get
            Return _ctor_ticks
        End Get
        Private Set(ByVal value As Int64)
            _ctor_ticks = value
        End Set
    End Property

    Private _begin_ticks As Int64 = npos
    Public Property begin_ticks As Int64
        Get
            Return _begin_ticks
        End Get
        Private Set(ByVal value As Int64)
            If _begin_ticks = npos OrElse value = npos Then
                _begin_ticks = value
            End If
        End Set
    End Property

    Private _check_ticks As Int64 = npos
    Public Property check_ticks As Int64
        Get
            Return _check_ticks
        End Get
        Private Set(ByVal value As Int64)
            If _check_ticks = npos OrElse value = npos Then
                _check_ticks = value
            End If
        End Set
    End Property

    Private _end_ticks As Int64 = npos
    Public Property end_ticks As Int64
        Get
            Return _end_ticks
        End Get
        Private Set(ByVal value As Int64)
            assert(_end_ticks = npos OrElse value = npos)
            _end_ticks = value
        End Set
    End Property

    Private _finish_ticks As Int64 = npos
    Public Property finish_ticks As Int64
        Get
            Return _finish_ticks
        End Get
        Private Set(ByVal value As Int64)
            assert(_finish_ticks = npos OrElse value = npos)
            _finish_ticks = value
        End Set
    End Property

    <ThreadStatic()> Private Shared _current As queue(Of callbackAction) = Nothing
    Public Shared Property current As callbackAction
        Get
            assert(Not _current Is Nothing AndAlso _current.size() > 0 AndAlso Not _current.back() Is Nothing)
            Return _current.back()
        End Get
        Protected Set(ByVal value As callbackAction)
            'removed, since callbackManager2 will run several chained callbackActions in one thread
            'assert(value Is Nothing OrElse _current Is Nothing OrElse object_compare(value, _current) = 0)
            If _current Is Nothing Then
                _current = New queue(Of callbackAction)()
            End If
            If value Is Nothing Then
                assert(_current.pop())
            Else
                assert(_current.push(value))
            End If
        End Set
    End Property

    Friend Shared Function getCurrent() As callbackAction
        If _current Is Nothing OrElse _current.size() = 0 Then
            Return Nothing
        Else
            Return _current.back()
        End If
    End Function

    Public Sub cancel(Optional ByVal beginResult As Boolean = True, Optional ByVal endResult As Boolean = True)
        begin = do_(beginResult)
        check = do_(check_pass)
        [end] = do_(endResult)
    End Sub

    Public Overridable Sub reset()
        begin_ticks() = npos
        check_ticks() = npos
        end_ticks() = npos
        finish_ticks() = npos
        to_begining()
        begin_result() = ternary.unknown
        check_result() = ternary.unknown
        end_result() = ternary.unknown
        _manager = Nothing
    End Sub

    Public Sub reset(ByVal timeoutticks As Int64)
        reset()
        Me.timeoutticks = timeoutticks
    End Sub

    Protected Function callstack() As String
        Return _callstack
    End Function

    Friend Function timeout() As Boolean
        Return timeoutticks >= 0 AndAlso (Now.Ticks() - begin_ticks()) >= timeoutticks
    End Function

    Private Function action(ByVal d As _do(Of Boolean), ByVal [step] As String) As Boolean
        assert(Not d Is Nothing)
        Dim rtn As Boolean = Nothing
        Dim startticks As Int64 = 0
        startticks = Now.Ticks()
        current() = Me
        timelimitOperation.start()
        rtn = do_(d, False)
        timelimitOperation.finish(callstack(), ":", [step])
        current() = Nothing
        Return rtn
    End Function

    Friend Sub action_begin()
        begin_ticks() = Now.Ticks()
        begin_result() = action(begin, "begin")
        If begin_result().true_() Then
            to_pending()
        Else
            raiseError(errorHandle.errorType.warning, "action ", callstack(), ":begin failed.")
            to_ending()
        End If
    End Sub

    Friend Sub action_check()
        check_ticks() = Now.Ticks()
        check_result() = action(check, "check")
        If check_result() = check_pass Then
            to_ending()
        ElseIf timeout() Then
            raiseError(errorHandle.errorType.warning, "action ", callstack(), " timeout.")
            to_ending()
        End If
    End Sub

    Friend Sub action_end()
        end_ticks() = Now.Ticks()
        end_result() = action([end], "end")
        to_finished()
        finish_ticks() = Now.Ticks()
    End Sub

    <Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
    Protected Sub ctor(ByVal begin As _do(Of Boolean),
                       ByVal check As _do(Of Boolean),
                       ByVal [end] As _do(Of Boolean),
                       ByVal timeoutticks As Int64,
                       ByVal additionalJump As Int64)
        Me.begin = ifoper(begin Is Nothing, do_(True), begin)
        Me.check = ifoper(check Is Nothing, do_(check_pass), check)
        Me.end = ifoper([end] Is Nothing, do_(True), [end])
        Me.ctor_ticks() = Now.Ticks()
        Me.timeoutticks = timeoutticks
        If isdebugmode() Then
            Me._callstack = callingcode(additionalJump + 1)
        Else
            Me._callstack = "##NOT_TRACE##"
        End If
        reset()
    End Sub

    <Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
    Public Sub New(Optional ByVal begin As _do(Of Boolean) = Nothing,
                   Optional ByVal check As _do(Of Boolean) = Nothing,
                   Optional ByVal [end] As _do(Of Boolean) = Nothing,
                   Optional ByVal timeoutticks As Int64 = npos)
        ctor(begin, check, [end], timeoutticks, 1)
    End Sub

    'avoid cloneable
    Public Function Clone() As Object Implements ICloneable.Clone
        Return Me
    End Function
End Class
